// @author A0101291X
#include <QCompleter>
#include <qstring.h>
#include <qstringlist.h>
#include <qdatetime.h>
#include <qshortcut.h>
#include "gui.h"

const string GUI::CONFIG_FILENAME = "PowerScheduleConfig.txt";
const QString GUI::MESSAGE_WELCOME = "Welcome to PowerSchedule! ";
const QString GUI::MESSAGE_NO_TASK_TODAY = "You have no tasks for today!";
const QString GUI::MESSAGE_INDICATE_TODAY = "Today's tasks:";
const QString GUI::MESSAGE_INDICATE_SEARCH = "Search result for ";
const QString GUI::MESSAGE_INDICATE_ALL = "Displaying all tasks:";

const QString GUI::STRING_F1_HELP = "F1 for Help";
const QString GUI::HEADER_NUMBER = "No.";
const QString GUI::HEADER_DESCRIPTION = "My Tasks";
const QString GUI::HEADER_DATE = "Date";
const QString GUI::HEADER_START = "Start Time";
const QString GUI::HEADER_END = "End Time";
const QString GUI::HEADER_TAG = "Tag";
const QString GUI::HEADER_DONE = "Done";

GUI::GUI(QWidget *parent) : QMainWindow(parent), helpDialog(this) {
	ui.setupUi(this);

	initialiseConfig();
	initialiseTimer();
	initialiseColourScheme();
	initialiseShortcuts();
	initialUISetup();
	welcomeDisplay();
}

GUI::~GUI() {
	writeConfigFile();
	configFile.close();
	delete timer;
}

//@author A0096950U
void GUI::initialiseTimer() {		
	timer = new QTimer(this);		
	connect(timer, SIGNAL(timeout()), this, SLOT(refreshDisplay()));		
	timer->start(10000);		
}		

void GUI::refreshDisplay() {
	setDateTime();
	updateDisplay();		
	showTasksToUser();		
	showDoneToUser();		
}

void GUI::initialiseConfig() {
	configFile.open(CONFIG_FILENAME);
	if(!configFile.good()) {
		configFile.close();
		ofstream newFile(CONFIG_FILENAME);
		configFile.open(CONFIG_FILENAME);
		newFile.close();
		helpFunction();
		currentTheme = 0;
		is24HourFormat = false;
		isDateExpanded = true;
		writeConfigFile();
	}
	else {
		readConfigFile();
	}
}

void GUI::readConfigFile() {
	string line;
	string data;

	getline(configFile, line);
	data = line.substr(line.find_first_of(Utilities::STRING_WHITESPACE) + 1);
	currentTheme = stoi(data);

	getline(configFile, line);
	data = line.substr(line.find_first_of(Utilities::STRING_WHITESPACE) + 1);
	is24HourFormat = stoi(data);

	getline(configFile, line);
	data = line.substr(line.find_first_of(Utilities::STRING_WHITESPACE) + 1);
	isDateExpanded = stoi(data);
}

//@author A0101291X
void GUI::writeConfigFile() {
	configFile.close();
	configFile.open(CONFIG_FILENAME, ios::out | ios::trunc);
	configFile.close();
	configFile.open(CONFIG_FILENAME);

	configFile << "Colour" << Utilities::STRING_WHITESPACE << currentTheme << endl; 
	configFile << "24HrTime" << Utilities::STRING_WHITESPACE << is24HourFormat << endl;
	configFile << "ExpandedDateFormat" << Utilities::STRING_WHITESPACE << isDateExpanded << endl;
	configFile.flush();
}

void GUI::initialiseShortcuts() {
	new QShortcut(Qt::Key_F1, this, SLOT(helpFunction()));
	new QShortcut(Qt::CTRL + Qt::Key_F8, this, SLOT(changeTheme()));
	new QShortcut(Qt::CTRL + Qt::Key_F7, this, SLOT(changeTimePreference()));
	new QShortcut(Qt::CTRL + Qt::Key_F6, this, SLOT(changeDatePreference()));
	new QShortcut(Qt::Key_Tab, this, SLOT(moveCursor()));
}

void GUI::initialUISetup() {
	setInputAsInvalid();
	setUIComponents();
	setTitle();
	setDateTime();
	setStyleSheet();
	setTaskTable();
	setDoneTable();
	setCommandList();
	setCompleter();
	on_commandLine_textChanged();
}

void GUI::setInputAsInvalid() {
	isInputValid = false;
}

void GUI::setUIComponents() {
	ui.toDoTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
	ui.doneTable->setEditTriggers(QAbstractItemView::NoEditTriggers);

	ui.toDoTable->setSortingEnabled(false);
	ui.doneTable->setSortingEnabled(false);

	greenTick.load(":/IconFiles/Resources/Green_Tick.png");
	redCross.load(":/IconFiles/Resources/Red_Cross.png");

	greenTick = greenTick.scaled(ui.validLabel->size(), Qt::KeepAspectRatio);
	redCross = redCross.scaled(ui.validLabel->size(), Qt::KeepAspectRatio);
}

void GUI::setTitle() {
	QWidget::window()->setWindowTitle("PowerSchedule");
	QString titleColour;
	titleColour = "<font color=\"" + themes.getColour1(currentTheme) + "\">Power</font>";
	titleColour += "<font color=\"" + themes.getColour2(currentTheme) + "\">Schedule</font>";

	ui.titleLabel->setText(titleColour);
}

void GUI::setDateTime() {
	QString dateTime = UIDate() + QString::fromStdString(Utilities::STRING_WHITESPACE) + UITime();
	dateTime = "<font color=\"" + themes.getColour1(currentTheme) + "\">" + dateTime + "</font>";
	ui.timeLabel->setText(dateTime);
}

QString GUI::UIDate() {
	QString dateToday = QDate::currentDate().toString();
	QString day = dateToday.mid(0,3);
	QString month = dateToday.mid (4,3);
	QString date = dateToday.mid(8,2);
	QString year = dateToday.mid(11,4);

	dateToday = day + ", " + date + QString::fromStdString(Utilities::STRING_WHITESPACE) 
		+ month + QString::fromStdString(Utilities::STRING_WHITESPACE) 
		+ year;
	return dateToday;
}

QString GUI::UITime() {
	QString currentTime;
	if(is24HourFormat){
		currentTime = QTime::currentTime().toString("hh:mm");
	} else {
		currentTime = QTime::currentTime().toString("hh:mm A");
	}
	return currentTime;
}

void GUI::setCommandList() {
	QString listOfCommand;
	listOfCommand = "<font color=\"" + themes.getColour1(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_ADD) + " </font>";
	listOfCommand +=  "<font color=\"" + themes.getColour2(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_SEARCH) + "  </font>";
	listOfCommand += "<font color=\"" + themes.getColour1(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_DELETE) + "  </font>";
	listOfCommand +=  "<font color=\"" + themes.getColour2(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_MODIFY) + "  </font>";
	listOfCommand += "<font color=\"" + themes.getColour1(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_DONE) +  "  </font>";
	listOfCommand +=  "<font color=\"" + themes.getColour2(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_UNDO) + "  </font>";
	listOfCommand +=  "<font color=\"" + themes.getColour1(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_TODAY) + "  </font>";
	listOfCommand +=  "<font color=\"" + themes.getColour2(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_ALL) + "  </font>";
	listOfCommand += "<font color=\"" + themes.getColour1(currentTheme) + "\"> " + QString::fromStdString(Utilities::STRING_EXIT) + "  </font>";

	ui.commandList->setText(listOfCommand);

	QString F1help = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + STRING_F1_HELP + "</font>";
	ui.helpLabel->setText(F1help);
}

void GUI::setTaskTable() {
	ui.toDoTable->setColumnCount(6);

	QStringList tableHeader;
	tableHeader << HEADER_NUMBER << HEADER_DESCRIPTION << HEADER_DATE << HEADER_START << HEADER_END << HEADER_TAG;
	ui.toDoTable->setHorizontalHeaderLabels(tableHeader);

	ui.toDoTable->setColumnWidth(TASK_NUMBER,32);
	ui.toDoTable->setColumnWidth(TASK_DESCRIPTION,170);
	ui.toDoTable->setColumnWidth(DATE,100);
	ui.toDoTable->setColumnWidth(START_TIME,80);
	ui.toDoTable->setColumnWidth(END_TIME,80);
	ui.toDoTable->setColumnWidth(TAG,80);
}

void GUI::setDoneTable() {
	ui.doneTable->setColumnCount(2);

	QStringList tableHeader;
	tableHeader << HEADER_NUMBER << HEADER_DONE;
	ui.doneTable->setHorizontalHeaderLabels(tableHeader);

	ui.doneTable->setColumnWidth(TASK_NUMBER,32);
}

QString GUI::setTimeFormat(QString timeToFormat) {
	QString timeToReturn = timeToFormat;
	if(!timeToFormat.isEmpty()) {
		if(!is24HourFormat) {
			int time = timeToFormat.toInt();
			timeToReturn = QString::fromStdString(Utilities::convertTo12Hr(time));
		}
		timeToReturn = timeToReturn.insert(2, QString(":"));
	}
	return timeToReturn;
}

QString GUI::setDateFormat(QString dateToFormat) {
	QString dateToReturn = dateToFormat;
	if(!dateToFormat.isEmpty()) {
		if(isDateExpanded) {
			QString day = dateToFormat.mid(0,2);
			int intMonth = dateToFormat.mid(2,2).toInt();
			QString month = QString::fromStdString(Utilities::monthInWords(intMonth));
			QString year = dateToFormat.mid(4,4);

			dateToReturn = day + QString::fromStdString(Utilities::STRING_WHITESPACE) + 
				month + QString::fromStdString(Utilities::STRING_WHITESPACE) +
				year;
		} else {
			dateToReturn = dateToReturn.insert(4, QString("/"));
			dateToReturn = dateToReturn.insert(2, QString("/"));
		}
	}
	return dateToReturn;
}

void GUI::setStyleSheet() {
	QString style;

	style = "QTableView {border-color: " + themes.getColour1(currentTheme) + ";" + 
		"background-color: " + themes.getTableColour(currentTheme) + ";}\n";

	style += "QLineEdit {border-color: " + themes.getColour2(currentTheme) + ";}\n";
	style += "#frame {background: " + themes.getFrameColour(currentTheme) + ";}\n";

	ui.centralWidget->setStyleSheet(style);
}

void GUI::initialiseColourScheme() {
	themes.addTheme("#FFAB00","#06246F", "#D8D8D8", "white");	//orange-blue
	themes.addTheme("#F80012","#FFF700","white", "#F88379");	//pink-yellow
	themes.addTheme("#4E9600", "#6C006C", "white", "#D8D8D8");	//green-purple
	themes.addTheme("#1240AB","black", "white", "#33CDC7");		//blue
	themes.addTheme("#F46F1B","#FF0700", "#D8D8D8", "#FFE5B4");	//orange
	themes.addTheme("#191970","#1F75FE", "#B0E0E6", "white");	//white-light blue
	themes.addTheme("#2F847C","black", "white", "#ACE1AF");		//green
}

void GUI::setIconValid() {
	ui.validLabel->setPixmap(greenTick);
}

void GUI::setIconInvalid() {
	ui.validLabel->setPixmap(redCross);
}

//Program Execution
void GUI::welcomeDisplay() {
	refreshDisplay();
	QString welcomeMessage;
	if(mainDisplay.empty()) {
		welcomeMessage = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + MESSAGE_WELCOME + MESSAGE_NO_TASK_TODAY + "</font>";
	} else {
		welcomeMessage = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + MESSAGE_WELCOME + "</font>";
	}
	ui.feedbackLabel->setText(welcomeMessage);
}

void GUI::updateDisplay() {
	logic.updateStatus();
	mainDisplay = logic.getMainDisplay();
	doneDisplay = logic.getDoneDisplay();

	ui.toDoTable->clearContents();
	ui.toDoTable->setRowCount(0);
	ui.doneTable->clearContents();
	ui.doneTable->setRowCount(0);
	indicateDisplayType();
}

void GUI::indicateDisplayType() {
	QString displayType;
	if (!logic.isSearchResultDisplay()) {
		displayType = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + MESSAGE_INDICATE_TODAY + "</font>";
	} else {
		QString searchKeyword = QString::fromStdString(logic.getSearchKeyword());

		if(searchKeyword.isEmpty()){
			displayType = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + MESSAGE_INDICATE_ALL + "</font>";
		} else {
			displayType = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + MESSAGE_INDICATE_SEARCH + "\"" + searchKeyword + "\"" + "</font>";
		}
	}
	ui.indicator->setText(displayType);
}

void GUI::displayFeedback() {						
	QString message = QString::fromStdString(logic.getFeedback());			
	QString feedback = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + message + "</font>";
	ui.feedbackLabel->setText(feedback);
}

void GUI::displayFeedback(QString errorMessage) {
	QString feedback = "<font color=\"red\">" + errorMessage + "</font>";
	ui.feedbackLabel->setText(feedback);
}

void GUI::showTasksToUser() {

	for (unsigned i = 0; i < mainDisplay.size(); i++) {
		QString taskNumber = QString::number(i+1);
		QString description = QString::fromStdString(mainDisplay[i].getDescription());
		QString date = QString::fromStdString(mainDisplay[i].getDate());
		QString startTime = QString::fromStdString(mainDisplay[i].getStartTime());
		QString endTime = QString::fromStdString(mainDisplay[i].getEndTime());
		QString tag = QString::fromStdString(mainDisplay[i].getTag());

		date = setDateFormat(date);
		startTime = setTimeFormat(startTime);
		endTime = setTimeFormat(endTime); 

		TaskStatus taskStatus = mainDisplay[i].getStatus();

		ui.toDoTable->insertRow(ui.toDoTable->rowCount());
		setTaskItem(i, TASK_NUMBER, taskNumber, ALIGN_CENTER, taskStatus);
		setTaskItem(i, TASK_DESCRIPTION, description, ALIGN_LEFT, taskStatus);
		setTaskItem(i, DATE, date, ALIGN_CENTER, taskStatus);
		setTaskItem(i, START_TIME, startTime, ALIGN_CENTER, taskStatus);
		setTaskItem(i, END_TIME, endTime, ALIGN_CENTER, taskStatus);
		setTaskItem(i, TAG, tag, ALIGN_CENTER, taskStatus);
	}
}

void GUI::showDoneToUser() {
	for (unsigned i = 0; i < doneDisplay.size(); i++) {
		QString taskNumber = QString::number(i+1) + ".";
		QString description = QString::fromStdString(doneDisplay[i].getDescription());

		ui.doneTable->insertRow(ui.doneTable->rowCount());
		setDoneItem(i, TASK_NUMBER, taskNumber, ALIGN_CENTER);
		setDoneItem(i, TASK_DESCRIPTION, description, ALIGN_LEFT);
	}
}

void GUI::setTaskItem(int row, int column, QString details, Alignment align, TaskStatus taskStatus) {

	QTableWidgetItem *item = new QTableWidgetItem(details);

	if(taskStatus == TASK_ONGOING) {
		item->setBackgroundColor(QColor(180,238,180));
	} else if(taskStatus == TASK_OVERDUE) {
		item->setBackgroundColor(QColor(255,193,193));
	}
	ui.toDoTable->setItem(row,column, item);

	if(align == ALIGN_CENTER) {
		ui.toDoTable->item(row, column)->setTextAlignment(Qt::AlignCenter);
	}
}

void GUI::setDoneItem(int row, int column, QString details, Alignment align) {
	ui.doneTable->setItem(row,column, new QTableWidgetItem(details));

	if(align == ALIGN_CENTER) {
		ui.doneTable->item(row, column)->setTextAlignment(Qt::AlignCenter);
	}
}

void GUI::on_commandLine_returnPressed() {
	if(isInputValid) {
		QString commandEntered = ui.commandLine->text();
		QString toModify;
		string message;

		try {
			message = logic.processUserCommand(commandEntered.toStdString());

			if(message == Utilities::STRING_EXIT) {
				QCoreApplication::quit();
			}

			toModify = QString::fromStdString(message);
			refreshDisplay();
			displayFeedback();
		} catch (std::runtime_error &error) {
			displayFeedback(QString::fromStdString(error.what()));
		} 

		ui.commandLine->clear();
		ui.commandLine->setText(toModify);
	} else {
		displayFeedback(QString::fromStdString(logic.getFeedback()));
	}
}

void GUI::clearValidLabel() {		
	ui.validLabel->clear();		
}

//@author A0096950U
void GUI::on_commandLine_textChanged() {

	string userInput = (ui.commandLine->text()).toStdString();

	isInputValid = logic.isValidInput(userInput);

	if(userInput.empty()) {
		clearValidLabel();
	} else {
		if(isInputValid) {
			setIconValid();
		} else {
			setIconInvalid();
		}
	}
}

// @author A0101291X
void GUI::changeTheme() {
	currentTheme++;
	currentTheme = currentTheme % themes.size();

	initialUISetup();
	indicateDisplayType();
	updateFeedbackColour();
}

void GUI::updateFeedbackColour() {
	string feedback = ui.feedbackLabel->text().toStdString();
	feedback = feedback.substr(feedback.find_first_of(">") + 1);
	feedback = feedback.substr(0,feedback.find_first_of("<"));

	QString feedbackToSet = "<font color=\"" + themes.getColour2(currentTheme) + "\">" + QString::fromStdString(feedback) + "</font>";
	ui.feedbackLabel->setText(feedbackToSet);
}

void GUI::changeTimePreference() {
	is24HourFormat = !is24HourFormat;
	setDateTime();
	refreshDisplay();
}

void GUI::changeDatePreference() {
	isDateExpanded = !isDateExpanded;
	refreshDisplay();
}

void GUI::helpFunction() {
	if(!helpDialog.isVisible()) {
		helpDialog.show();
	} else {
		helpDialog.hide();
	}
}

//@author A0096950U
void GUI::setCompleter() {

	string skeleton;
	skeleton += Utilities::STRING_BLOCK_OPEN;
	skeleton += Utilities::STRING_DEFAULT_DESCRIPTION;
	skeleton += Utilities::STRING_BLOCK_CLOSE;

	skeleton += Utilities::STRING_BLOCK_OPEN;
	skeleton += Utilities::STRING_DEFAULT_DATE;
	skeleton += Utilities::STRING_BLOCK_CLOSE;

	skeleton += Utilities::STRING_BLOCK_OPEN;
	skeleton += Utilities::STRING_DEFAULT_START_TIME;
	skeleton += Utilities::STRING_BLOCK_CLOSE;

	skeleton += Utilities::STRING_BLOCK_OPEN;
	skeleton += Utilities::STRING_DEFAULT_END_TIME;
	skeleton += Utilities::STRING_BLOCK_CLOSE;

	skeleton += Utilities::STRING_BLOCK_OPEN;
	skeleton += Utilities::STRING_DEFAULT_TAG;
	skeleton += Utilities::STRING_BLOCK_CLOSE;

	string skeleton_add = Utilities::STRING_ADD + Utilities::STRING_WHITESPACE;
	string skeleton_delete = Utilities::STRING_DELETE + Utilities::STRING_WHITESPACE;
	string skeleton_modify = Utilities::STRING_MODIFY + Utilities::STRING_WHITESPACE;
	string skeleton_search = Utilities::STRING_SEARCH + Utilities::STRING_WHITESPACE;
	string skeleton_done = Utilities::STRING_DONE + Utilities::STRING_WHITESPACE;
	string skeleton_today = Utilities::STRING_TODAY;
	string skeleton_undo = Utilities::STRING_UNDO;
	string skeleton_exit = Utilities::STRING_EXIT;
	string skeleton_all = Utilities::STRING_ALL;
	skeleton_add += skeleton;

	inputCompleter << QString::fromStdString(skeleton_add);
	inputCompleter << QString::fromStdString(skeleton_delete);
	inputCompleter << QString::fromStdString(skeleton_modify);
	inputCompleter << QString::fromStdString(skeleton_search);
	inputCompleter << QString::fromStdString(skeleton_done);
	inputCompleter << QString::fromStdString(skeleton_today);
	inputCompleter << QString::fromStdString(skeleton_undo);
	inputCompleter << QString::fromStdString(skeleton_exit);
	inputCompleter << QString::fromStdString(skeleton_all);

	completer = new QCompleter(inputCompleter, ui.frame);
	completer->setCaseSensitivity(Qt::CaseInsensitive);
	completer->setCompletionMode(QCompleter::InlineCompletion);
	ui.commandLine->setCompleter(completer);
}

void GUI::moveCursor() {
	string text = ui.commandLine->text().toStdString();

	string firstWord = Utilities::extractFirstWord(string(text));

	unsigned curPos = ui.commandLine->cursorPosition();
	if (firstWord == Utilities::STRING_ADD || firstWord == Utilities::STRING_TO) {
		text = text.substr(curPos);

		unsigned startPos = text.find_first_of(Utilities::STRING_BLOCK_OPEN);
		unsigned endPos = text.find_first_of(Utilities::STRING_BLOCK_CLOSE);

		if (startPos == string::npos) {
			text = ui.commandLine->text().toStdString();
			startPos = text.find_first_of(Utilities::STRING_BLOCK_OPEN);
			endPos = text.find_first_of(Utilities::STRING_BLOCK_CLOSE);
			curPos = 0;
		}

		if (endPos < startPos) {
			endPos =  text.find_first_of(Utilities::STRING_BLOCK_CLOSE, endPos+1);
		}

		ui.commandLine->setCursorPosition(curPos+startPos+1);
		ui.commandLine->cursorForward(true, endPos-startPos-1);
	} else {
		ui.commandLine->setCursorPosition(text.size());
	}
}